/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_Log_h__
#define __dylab_Log_h__

//===========================================================================================
#include <dylab/utils/Singleton.h>
#include <dylab/utils/containers/HashObjContainer.h>

//===========================================================================================
namespace dylab {
    
    /** 
     * Thread-safe logging.
     */    
    class DYLAB_EXPORT Log 
        : public Singleton<Log>
    {
    public:
        class DYLAB_EXPORT CallTracker
        {
        private:
            String mFunction;
        public:
            CallTracker(const String & _function, const String & _file, uint32_t _line);
            ~CallTracker();
        };
        //-----------------------------------------------------------------------------

    public:
        class DYLAB_EXPORT ObjectTracker
        {
        private:
            void * mObject;
        public:
            ObjectTracker(void * _instance, const String & _class, const String & _file);
            ~ObjectTracker();
        };
        //-----------------------------------------------------------------------------

    public:
        class DYLAB_EXPORT IListener
        {
        public:
            virtual ~IListener()
                    { }

            virtual void logMessage(const String & message) = 0;
        };
        //-----------------------------------------------------------------------------

    public:               
        class DYLAB_EXPORT Listeners 
            : public HashObjContainer<String, IListener> 
        { 
        public:
            Listeners()
                : HashObjContainer<String, IListener>(false)
                    { }
        };
        //-----------------------------------------------------------------------------

    private:
        /// the log object does not own the listeners, so they must be deleted manually when not needed!!!
        Listeners mLogListeners;

        // log message indentation - e.g., for function calls
        //TODO: hash map <thread id -> indentation> + log(thread id, message)
        uint32_t mIndentDepth;

    public:

        /**
         *
         */
        Log();

        /**
         *
         */
        virtual ~Log();

        /**
         * Registers the given listener under the specified name.
         */
        void registerListener(const String & _name, IListener * _listener);

        /**
         * Unregisters the listener previously registered under the specified name,
		 * and returns the pointer to it.
		 *
		 * @remarks	The log do not delete the listener object, 
		 *			you have to do it manually.
         */
        IListener * unregisterListener(const String & _name);

        /**
         *
         */
        void log(const String & _message);

        /**
         *
         */
        void indent();

        /**
         *
         */
        void unindent();

    private:

        /**
         * TODO: mel by mit jeste parametr s id vlakna
         */
        void _log(const String & message);
    };
}

//===========================================================================================
// basic log macro
#define DYLAB_LOG(msg)	DYLAB_SINGLETON_SCOPED_EX_LOCK(Log)->log(msg)

// log with formating
#define DYLAB_LOGF(f, v)				DYLAB_LOG(dylab::String::fmt(f, v))
#define DYLAB_LOGF2(f, v1, v2)			DYLAB_LOG(dylab::String::fmt(f, v1, v2))
#define DYLAB_LOGF3(f, v1, v2, v3)		DYLAB_LOG(dylab::String::fmt(f, v1, v2, v3))
#define DYLAB_LOGF4(f, v1, v2, v3, v4)	DYLAB_LOG(dylab::String::fmt(f, v1, v2, v3, v4))

// log variants & helpers
#define DYLAB_LOG_EXP(e)           DYLAB_LOG(DYLAB_EXP_INFO(e))
#define DYLAB_LOG_OBJEXP(o)        DYLAB_LOG(DYLAB_OBJEXP_INFO(o))
#define DYLAB_LOG_CALLS            dylab::Log::CallTracker __dylabLogCallTracker(__FUNCTION__, __FILE__, __LINE__);
#define DYLAB_LOG_OBJECTS_DECL     dylab::Log::ObjectTracker __dylabLogObjectTracker;
#define DYLAB_LOG_OBJECTS_CTOR     __dylabLogObjectTracker(__CLASS__, __FILE__)

// trace macro - i.e., log only if in debug mode or when forced
#if defined(DYLAB_BUILD_DEBUG) || (DYLAB_FORCE_TRACE_LOGGING == 1)
#   define DYLAB_TRACE(m)					DYLAB_LOG(m)
#   define DYLAB_TRACEF(f, v)				DYLAB_LOGF(f, v)
#   define DYLAB_TRACEF2(f, v1, v2)			DYLAB_LOGF2(f, v1, v2)
#   define DYLAB_TRACEF3(f, v1, v2, v3)		DYLAB_LOGF3(f, v1, v2, v3)
#   define DYLAB_TRACEF4(f, v1, v2, v3, v4) DYLAB_LOGF4(f, v1, v2, v3, v4)
#   define DYLAB_TRACE_EXP(v)				DYLAB_LOG_EXP(v)
#   define DYLAB_TRACE_OBJEXP(o)			DYLAB_LOG_OBJEXP(o)
#   define DYLAB_TRACE_CALLS				DYLAB_LOG_CALLS
#   define DYLAB_TRACE_OBJECTS_DECL			DYLAB_LOG_OBJEXPS_DECL
#   define DYLAB_TRACE_OBJECTS_CTOR			DYLAB_LOG_OBJEXPS_CTOR
#else
#   define DYLAB_TRACE(m)
#   define DYLAB_TRACEF(f, v)
#   define DYLAB_TRACEF2(f, v1, v2)
#   define DYLAB_TRACEF3(f, v1, v2, v3)
#   define DYLAB_TRACEF4(f, v1, v2, v3, v4)
#   define DYLAB_TRACE_EXP(v)           
#   define DYLAB_TRACE_OBJEXP(o)        
#   define DYLAB_TRACE_CALLS     
#   define DYLAB_TRACE_OBJECTS_DECL
#   define DYLAB_TRACE_OBJECTS_CTOR   
#endif    

//===========================================================================================
#endif // __dylab_Log_h__
